package com.lap.framework.common.tool;

import com.lap.framework.common.exception.CryptoException;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.security.*;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;
import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import lombok.experimental.UtilityClass;

/**
 * 加密与编码工具类
 *
 * <p>提供对称加密(AES)、非对称加密(RSA)和URI编码功能
 *
 * @author Shuisheng Lao(劳水生)
 * @version 0.0.1
 */
@UtilityClass
public class CryptoUtil {

  // 对称加密算法 - AES
  private static final String AES_ALGORITHM = "AES/CBC/PKCS5Padding";
  private static final int AES_KEY_SIZE = 128;

  // 非对称加密算法 - RSA
  private static final String RSA_ALGORITHM = "RSA/ECB/PKCS1Padding";
  private static final int RSA_KEY_SIZE = 2048;

  /** 生成AES密钥 */
  public static String generateAESKey() {
    try {
      KeyGenerator keyGen = KeyGenerator.getInstance("AES");
      keyGen.init(AES_KEY_SIZE);
      SecretKey secretKey = keyGen.generateKey();
      return Base64.getEncoder().encodeToString(secretKey.getEncoded());
    } catch (NoSuchAlgorithmException e) {
      throw new CryptoException(e);
    }
  }

  /** AES加密 */
  public static String aesEncrypt(String data, String key) {
    byte[] keyBytes = Base64.getDecoder().decode(key);
    SecretKeySpec secretKeySpec = new SecretKeySpec(keyBytes, "AES");

    // 生成随机IV
    byte[] iv = new byte[16];
    SecureRandom random = new SecureRandom();
    random.nextBytes(iv);
    IvParameterSpec ivSpec = new IvParameterSpec(iv);
    try {
      Cipher cipher = Cipher.getInstance(AES_ALGORITHM);
      cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, ivSpec);

      byte[] encrypted = cipher.doFinal(data.getBytes(StandardCharsets.UTF_8));

      // 将IV和加密数据组合在一起
      byte[] combined = new byte[iv.length + encrypted.length];
      System.arraycopy(iv, 0, combined, 0, iv.length);
      System.arraycopy(encrypted, 0, combined, iv.length, encrypted.length);

      return Base64.getEncoder().encodeToString(combined);
    } catch (Exception e) {
      throw new CryptoException(e);
    }
  }

  /** AES解密 */
  public static String aesDecrypt(String encryptedData, String key) {
    byte[] keyBytes = Base64.getDecoder().decode(key);
    SecretKeySpec secretKeySpec = new SecretKeySpec(keyBytes, "AES");

    byte[] combined = Base64.getDecoder().decode(encryptedData);

    // 提取IV
    byte[] iv = new byte[16];
    System.arraycopy(combined, 0, iv, 0, iv.length);
    IvParameterSpec ivSpec = new IvParameterSpec(iv);

    // 提取加密数据
    byte[] encrypted = new byte[combined.length - 16];
    System.arraycopy(combined, 16, encrypted, 0, encrypted.length);
    try {
      Cipher cipher = Cipher.getInstance(AES_ALGORITHM);
      cipher.init(Cipher.DECRYPT_MODE, secretKeySpec, ivSpec);

      byte[] decrypted = cipher.doFinal(encrypted);
      return new String(decrypted, StandardCharsets.UTF_8);
    } catch (Exception e) {
      throw new CryptoException(e);
    }
  }

  /** 生成RSA密钥对 */
  public static KeyPair generateRSAKeyPair() {
    try {
      KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance("RSA");
      keyPairGen.initialize(RSA_KEY_SIZE);
      return keyPairGen.generateKeyPair();
    } catch (Exception e) {
      throw new CryptoException(e);
    }
  }

  /** RSA公钥加密 */
  public static String rsaEncrypt(String data, PublicKey publicKey) {
    try {
      Cipher cipher = Cipher.getInstance(RSA_ALGORITHM);
      cipher.init(Cipher.ENCRYPT_MODE, publicKey);

      byte[] encrypted = cipher.doFinal(data.getBytes(StandardCharsets.UTF_8));
      return Base64.getEncoder().encodeToString(encrypted);
    } catch (Exception e) {
      throw new CryptoException(e);
    }
  }

  /** RSA私钥解密 */
  public static String rsaDecrypt(String encryptedData, PrivateKey privateKey) {
    try {
      Cipher cipher = Cipher.getInstance(RSA_ALGORITHM);
      cipher.init(Cipher.DECRYPT_MODE, privateKey);

      byte[] data = Base64.getDecoder().decode(encryptedData);
      byte[] decrypted = cipher.doFinal(data);
      return new String(decrypted, StandardCharsets.UTF_8);
    } catch (Exception e) {
      throw new CryptoException(e);
    }
  }

  /** 从Base64字符串恢复公钥 */
  public static PublicKey getPublicKeyFromString(String key) {
    try {
      byte[] keyBytes = Base64.getDecoder().decode(key);
      X509EncodedKeySpec spec = new X509EncodedKeySpec(keyBytes);
      java.security.KeyFactory kf = java.security.KeyFactory.getInstance("RSA");
      return kf.generatePublic(spec);
    } catch (Exception e) {
      throw new CryptoException(e);
    }
  }

  /** 从Base64字符串恢复私钥 */
  public static PrivateKey getPrivateKeyFromString(String key) {
    try {
      byte[] keyBytes = Base64.getDecoder().decode(key);
      PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(keyBytes);
      java.security.KeyFactory kf = java.security.KeyFactory.getInstance("RSA");
      return kf.generatePrivate(spec);
    } catch (Exception e) {
      throw new CryptoException(e);
    }
  }

  /** URI编码 */
  public static String uriEncode(String value) {
    try {
      return URLEncoder.encode(value, StandardCharsets.UTF_8);
    } catch (Exception e) {
      throw new RuntimeException("Failed to encode URI", e);
    }
  }

  /** URI解码 */
  public static String uriDecode(String value) {
    try {
      return URLDecoder.decode(value, StandardCharsets.UTF_8);
    } catch (Exception e) {
      throw new RuntimeException("Failed to decode URI", e);
    }
  }
}
